import ij.ImagePlus;
import ij.plugin.PlugIn;
import ij.process.ColorProcessor;
import ij.process.ImageProcessor;


import java.awt.Color;


import java.awt.Font;

import java.awt.Polygon;
import java.io.PrintStream;

import java.util.Locale;
import java.util.Random; 



import pluginUtils.Circle;
import pluginUtils.Complexe; 
import pluginUtils.DoyleCircle;
import pluginUtils.MobiusTransform;
import pluginUtils.CircleInversion;
import pluginUtils.Monome;
import pluginUtils.Polynome;
import pluginUtils.Pos3D;
import pluginUtils.Sphere;
import pluginUtils.SphereInversion;

// Face centered cubic
public class PavageFCCNewton extends ImagePlus implements PlugIn{
	{Locale.setDefault(Locale.US); }
	
	private ImageProcessor primeIP; 
	private int X=2000,Y=2000; // image size
	private double rayon=0.025; 
	
	private double jump=6.0*Math.sqrt(2.0/3)*rayon; 
	
	
	private PrintStream output; 
	private Random generator=new Random(); // One always needs some randomness
	
	// For the scaling of the image
	private double x0=0,x1=1,y0=0,y1=1;
	private double acoef,ccoef,vcoef,wcoef; 
	
	private float[] palette={0.3f,0.8f,0.9f,0.2f,0.4f,0.6f,0.5f,0.2f,0.7f,0.0f,0.3f,0.8f,0.1f,0.2f,0.9f,0.1f};
	private int taille=palette.length;
	private Color[] palette1={Color.YELLOW,Color.RED,Color.BLUE,Color.GREEN,Color.MAGENTA,Color.ORANGE};
	public void setCoefs(){
		acoef=X/(x1-x0); 
		vcoef=Y/(y1-y0); 
		ccoef=-acoef*x0; 
		wcoef=-vcoef*y0; 
	}
	 
	private int maxInt=2560;
	
	private Polynome fonct=new Polynome();
	
	private Complexe fonctp(Complexe z){
		Complexe z1=Complexe.mul(z,z);
		return Complexe.mulbyDouble(z1, 3); 
	}
	
	private Complexe fonctpp(Complexe z){
		return Complexe.mulbyDouble(z, 6); 
	}
	
	private Complexe fonct(Complexe z){
		Complexe z1=Complexe.mul(z,z);  
		Complexe terme=Complexe.mul(z1,z);
		terme=Complexe.sub(terme,Complexe.UN);
		return terme;
	}
	
	private double a0=1; 
	private double a1=0.5; 
	private Complexe approx(Complexe z){
		Complexe fz=fonct.eval(z);
		Complexe fpz=fonct.derive().eval(z); 
		Complexe fpp=fonct.dSecond().eval(z);
		Complexe numer=Complexe.mul(fz,fpz); 
		Complexe d1=Complexe.mul(fpz,fpz); 
		d1=Complexe.mulbyDouble(d1, a0); 
		Complexe d2=Complexe.mul(fz,fpp); 
		d2=Complexe.mulbyDouble(d2, a1); 
		d1=Complexe.sum(d1,d2); 
		Complexe t1=Complexe.div(numer,d1);
		return Complexe.sub(z,t1);
		
	}
	
	private float entropy(float x){
		double xx=x+0.0;
		double terme=-xx*Math.log(xx)-(1-xx)*Math.log(1-xx); 
		terme=terme/Math.log(2);
		return (float)terme;
	}
	
	
	
	private Complexe j=Complexe.createComplexeFromRI(-0.5, Math.sqrt(3)/2); 
	private Complexe mj=Complexe.createComplexeFromRI(-0.5, -Math.sqrt(3)/2); 
	
	
	
	private Color getColor(Pos3D centrum){
		Complexe z=Complexe.createComplexeFromRI(centrum.getX(),centrum.getZ());
		
		int i;
		float coefR=1.0f; 
		float coefG=10.0f; 
		float coefB=100.0f; 
		int maxi=50;
		for(i=0;i<maxInt;i++){
			if(Complexe.distance(z,Complexe.UN)<1e-16) break;
			if(Complexe.distance(z,j)<1e-16) {coefR=10.0f;coefG=100.0f; coefB=1.0f; break;}
			if(Complexe.distance(z,mj)<1e-16) {coefR=10.0f;coefG=1.0f; coefB=100.0f; break;}
			z=approx(z);
		}
		
		if(i==maxInt) return new Color(1.0f,1.0f,1.0f);
		
		if(i!=maxInt) System.out.println(i);
		
		//return new Color(palette[i % taille],palette[(3*i)%taille],palette[(i*i)%taille]);
		//return new Color(1.3f*entropy((i+0.0f)/(coefR*maxInt+0.0f)),1.3f*entropy((i+0.0f)/(coefG*maxInt+0.0f)),1.3f*entropy((i+0.0f)/(coefB*maxInt+0.0f)));
		return new Color((i+0.0f)/(coefR*maxi+0.0f),(i+0.0f)/(coefG*maxi+0.0f),(i+0.0f)/(coefB*maxi+0.0f));
	}
	
	
	public void setBounds(double xl,double xu,double yl,double yu){
		x0=xl; 
		x1=xu;
		y0=yl;
		y1=yu; 
	}
	
	
	
	private static int number=0; 
	
	private void drawCircle(Circle c){
		int intX=(int)(c.getXcenter()*acoef+ccoef);
		int intY=(int)(c.getYcenter()*vcoef+wcoef);
		int intR=(int)(c.getRadius()*acoef); // X ??
		primeIP.fillOval(intX-intR,intY-intR,intR*2,intR*2);
		
		}
		
	
	
	
	private void drawPoint(Complexe c){
		double xCenter=c.getReal(); 
		double yCenter=c.getIm(); 
		int intX=(int)(xCenter*acoef+ccoef);
		int intY=(int)(yCenter*vcoef+wcoef);
		primeIP.fillOval(intX-10,intY-10,20,20);
	}
	
	
	
	
	
	private void drawLine(double xd,double yd,double xf,double yf){
		int XD=(int)(xd*acoef+ccoef); 
		int YD=(int)(yd*vcoef+wcoef); 
		int XF=(int)(xf*acoef+ccoef); 
		int YF=(int)(yf*vcoef+wcoef);
		primeIP.drawLine(XD,YD,XF,YF); 
	}
	
	
	
	public void run(String arg) {
		primeIP = new ColorProcessor(X,Y);
		
		
		double limit=2.0; 
	
		setBounds(-limit,limit,-limit,limit); 
		setCoefs(); 
		
		
		fonct.add(new Monome(1,3)); 
		fonct.add(new Monome(-1,0));
		
		
		
		Polygon background=new Polygon(new int[]{0,0,X,Y},new int[]{0,X,Y,0},4);
		primeIP.setColor(Color.WHITE);
		
		primeIP.fillPolygon(background);
		
		
		primeIP.setFont(new Font(null,Font.ITALIC,60));
		primeIP.setColor(Color.BLACK);
		primeIP.setLineWidth(1); 
		
		
		// Inverter
		CircleInversion cinv=new CircleInversion(new Circle(Complexe.ZERO,0.1));
		//drawColoredCircle(cinv.getCi(),4);
		
		setProcessor("Verifying", primeIP);
		
		drawLine(x0,0,x1,0);
		drawLine(0,y0,0,y1);
		
		try{
			output=new PrintStream("/tmp/FCC2.txt"); 
		double horizon=limit; 
		double cx=-horizon;
		double cy; 
		boolean tic=false; 
		int color=0; 
		int cycle=0; 
		double rmax=0; 
		int ligne=0; 
		while(cx<horizon){
			System.out.println("--->"+ligne++);
			if(tic) cy=-horizon; else cy=-horizon+rayon;
			while(cy<horizon){
				for(int i=0;i<1;i++){
				
				
				Pos3D centrum=new Pos3D(cx,rayon+i*jump ,cy); 
				Circle c1=new Circle(centrum,rayon); 
				
				
				
				
				
				//output.println(cinv.inverte(c1));
				//c1=cinv.inverte(c1);
				Color current=getColor(centrum); 
				primeIP.setColor(current); 
				drawCircle(c1);
				double xCenter=c1.getXcenter(); 
				double yCenter=c1.getYcenter();
				String s="sphere{\n<"+xCenter+","+0+","+yCenter+">,";
				s+=c1.getRadius(); 
				if(c1.getRadius()>rmax) rmax=c1.getRadius(); 
				s+=" texture{pigment{color rgb "; //+palette[i]+"}} 
				s+="<"+(current.getRed()+0.0)/128.0+","+(current.getGreen()+0.0)/128.0+","+(current.getBlue()+0.0)/128.0+">}}";
				s+=" finish{reflection 0.6} }";
				//drawPoint(Complexe.createComplexeFromRI(centrum.getX(),centrum.getZ())); 
				output.println(s); 
				
				}
				cy+=rayon*2; 
				
				}// cy
			cycle=cycle+1; 
			cx=cx+Math.sqrt(3)*rayon; 
			tic=!tic; 
		
			//System.out.println(cx); 
		}// cx
		}
		catch (Exception e) {
			System.out.println("plante "+e.getMessage()); 
		}
		
		
		show();
		System.out.println("done "+number); 
	}	
}
	